package com.scau.os.fileSystem.memory;

import java.util.ArrayList;
import java.util.List;

import com.scau.os.fileSystem.other.MyFileSystemException;

public enum FAT {

    INSTANCE;

    public final int FREE = 0;
    public final int END_OF_FILE = -1;
    public final int DAMAGED = -2;

    private final int START_BLOCK = Disk.INSTANCE.ROOT_BLOCK + 1;
    private byte[] block0;
    private byte[] block1;

    private boolean isLoaded = false;
    private int lastAllocatedBlock = START_BLOCK;

    public void loadFromDisk() throws Exception {
        if (!isLoaded) {
            block0 = Disk.INSTANCE.readBlock(0);
            block1 = Disk.INSTANCE.readBlock(1);
            isLoaded = true;
        }
    }

    public void saveToDisk() throws Exception {
        if (isLoaded) {
            Disk.INSTANCE.writeBlock(0, block0);
            Disk.INSTANCE.writeBlock(1, block1);
            isLoaded = false;
        }
    }

    public void setFATItem(int blockNumber, int nextBlockNumber) throws Exception {
        loadFromDisk();
        int index = blockNumber / 2;
        int offset = blockNumber % 2;
        if (offset == 0) {
            block0[index] = (byte) nextBlockNumber;
        } else {
            block1[index] = (byte) nextBlockNumber;
        }
        saveToDisk();
    }

    public int getFATItem(int blockNumber) throws Exception {
        loadFromDisk();
        int index = blockNumber / 2;
        int offset = blockNumber % 2;
        if (offset == 0) {
            return block0[index];
        } else {
            return block1[index];
        }
    }

    public List<Integer> allocateBlock(int num) throws Exception {
        List<Integer> blocks = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            blocks.add(allocateBlock());
            if (i != 0) {
                setFATItem(blocks.get(i - 1), blocks.get(i));
            }
        }
        return blocks;
    }

    public int allocateBlock() throws Exception {
        for (int i = lastAllocatedBlock; i < Disk.INSTANCE.SIZE; i++) {
            if (getFATItem(i) == FREE) {
                setFATItem(i, END_OF_FILE);
                lastAllocatedBlock = i;
                return i;
            }
        }
        for (int i = START_BLOCK; i < lastAllocatedBlock; i++) {
            if (getFATItem(i) == FREE) {
                setFATItem(i, END_OF_FILE);
                lastAllocatedBlock = i;
                return i;
            }
        }

        throw new MyFileSystemException("磁盘空间不足");
    }

    public void freeBlock(int blockNumber) throws Exception {
        List<Integer> blocks = getBlocks(blockNumber);
        for (int i = 0; i < blocks.size(); i++) {
            setBlockStatus(blocks.get(i), FREE);
            Disk.INSTANCE.writeBlock(blocks.get(i), new byte[Disk.INSTANCE.BLOCK_SIZE]);
        }
    }

    public void damageBlock(int blockNumber) throws Exception {
        setBlockStatus(blockNumber, DAMAGED);
    }

    private void setBlockStatus(int blockNumber, int status) throws Exception {
        if (blockNumber < START_BLOCK || blockNumber >= Disk.INSTANCE.SIZE) {
            throw new IllegalArgumentException("Invalid block number: " + blockNumber);
        }

        setFATItem(blockNumber, status);
    }

    public List<Integer> getBlocks(int startBlockNumber) throws Exception {
        if (startBlockNumber < START_BLOCK - 1 || startBlockNumber >= Disk.INSTANCE.SIZE) {
            return null;
        }

        List<Integer> blocks = new ArrayList<>();
        int blockNumber = startBlockNumber;
        while (blockNumber != END_OF_FILE) {
            blocks.add(blockNumber);
            blockNumber = getFATItem(blockNumber);
        }
        return blocks;
    }

    public List<Boolean> FATStatus() throws Exception {
        List<Boolean> status = new ArrayList<>();
        for (int i = 0; i < Disk.INSTANCE.SIZE; i++) {
            if (getFATItem(i) == FREE) {
                status.add(true);
            } else {
                status.add(false);
            }
        }
        return status;
    }

}
